shell 字符串处理汇总(查找,替换等等)

您所在的位置:网站首页 sql 查找字符串出现的位置不一样怎么办 shell 字符串处理汇总(查找,替换等等)

shell 字符串处理汇总(查找,替换等等)

2024-07-10 23:18| 来源: 网络整理| 查看: 265

字符串:

简称“串”。有限字符的序列。数据元素为字符的线性表,是一种数据的逻辑结构。在计算机中可有不同的存储结构。在串上可进行求子串、插入字符、删除字符、置换字符等运算。

字符:

计算机程序设计及操作时使用的符号。包括字母、数字、空格符、提示符及各种专用字符等。

一般字符的运算包括:

 

第一、找出字符或者字符串的类型,是数字、字母还是其他特定字符,是可打印字符,还是不可打印字符(一些控制字符)。 第二、找出组成字符串的字符个数和字符串的存储结构(比如数组)。 第三、对串的常规操作:求子串、插入字符、删除字符、置换字符、字符串的比较等。 第四、对串的一些比较复杂而有趣的操作,这里将在最后介绍一些有趣的范例。

1. 字符串的属性

字符有可能是数字、字母、空格、其他特殊字符,而字符串有可能是它们任何一种或者多种的组合,在组合之后还可能形成一个具有特定意义的字符串,诸如邮件地址,URL地址等。概要示例: 下面我们来看看如何判断字符的类型。

 

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 // 数字或者数字组合(能够返回结果,即程序退出状态是0,说明属于这种类型,反之不然) $ i=5;j=9423483247234; $ echo $i | grep [0-9]* 5 $ echo $j | grep [0-9]* 9423483247234 $ echo $j | grep [0-9]* >/dev/null $ echo $? 0 // 字符组合(小写字母、大写字母、两者的组合) $ c="A"; d="fwefewjuew"; e="fewfEFWefwefe" $ echo $c | grep [A-Z] A $ echo $d | grep "[a-z]*" fwefewjuew $ echo $e | grep "[a-zA-Z]*" fewfEFWefwefe // 字母和数字的组合 $ ic="432fwfwefeFWEwefwef" $ echo $ic | grep "[0-9a-zA-Z]*" 432fwfwefeFWEwefwef // 空格或者Tab键等 $ echo " " | grep " "    $ echo -e "\t" | grep "[[:space:]]" #[[:space:]]会同时匹配空格和TAB键   $ echo -e " \t" | grep "[[:space:]]"   $ echo -e "\t" | grep "" #为在键盘上按下TAB键,而不是字符 // 匹配邮件地址 $ echo "[email protected]" | grep "[0-9a-zA-Z\.]*@[0-9a-zA-Z\.]" [email protected] // 匹配URL地址(以http链接为例) $ echo "http://news.lzu.edu.cn/article.jsp?newsid=10135" | grep "http://[0-9a-zA-Z\./=?]*" http://news.lzu.edu.cn/article.jsp?newsid=10135 说明:[1] /dev/null和/dev/zero是非常有趣的两个设备,它们都犹如一个黑洞,什么东西掉进去都会消失殆尽;后者则是一个能源箱,你总能从那里取到0,直到你退出。[2] [[:space:]]是grep用于匹配空格或者TAB键类型字符串的一种标记,其他类似的标记请查看grep的帮助,man grep。[3] 上面都是用grep来进行模式匹配,实际上sed, awk都可以用来做模式匹配,关于匹配中用到的正则匹配模式知识,大家可以参考正则匹配模式,更多相关资料请看参考资料。[4] 如果仅仅想判断字符串是否为空,即判断字符串的长度是否为零,那么可以简单的通过test命令的-z选项来判断,具体用法见test命令,man test.

概要示例: 判断字符是否可打印?如何控制字符在终端的显示。

 

 

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 用grep判断某个字符是否为可打印字符 $ echo "\t\n" | grep "[[:print:]]" \t\n $ echo $? 0 $ echo -e "\t\n" | grep "[[:print:]]" $ echo $? 1 // 用echo的-e选项在屏幕控制字符显示位置、颜色、背景等 $ echo -e "\33[31;40m" #设置前景色为黑色,背景色为红色 $ echo -e "\33[11;29H Hello, World\!" #在屏幕的第11行,29列开始打印字符串Hello,World! // 在屏幕的某个位置动态显示当前系统时间 $ while :; do echo -e "\33[11;29H "$(date "+%Y-%m-%d %H:%M:%S"); done // 用col命令过滤掉某些控制字符,在处理诸如script,screen等截屏命令的输出结果时,很有用 $ screen -L $ cat /bin/cat $ exit $ cat screenlog.0 | col -b   # 把一些控制字符过滤后,就可以保留可读的操作日志 更多关于字符在终端的显示控制方法,请参考资料[20]和字符显示实例[21]:用shell实现的一个动态时钟。

1.2 字符串的长度

概要示例: 除了组成字符串的字符类型外,字符串还有哪些属性呢?组成字符串的字符个数。下面我们来计算字符串的长度,即所有字符的个数,并简单介绍几种求字符串中指定字符个数的方法。

 

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 // 计算某个字符串的长度,即所有字符的个数[这计算方法是五花八门,择其优着而用之] $ var="get the length of me" $ echo ${var}     # 这里等同于$var get the length of me $ echo ${#var} 20 $ expr length "$var" 20 $ echo $var | awk '{printf("%d\n", length($0));}' 20 $ echo -n $var |  wc -c 20 // 计算某些指定一个字符或者多个字符的个数 $ echo $var | tr -cd g | wc -c 2 $ echo -n $var | sed -e 's/[^g]//g' | wc -c 2 $ echo -n $var | sed -e 's/[^gt]//g' | wc -c 5 // 如果要统计单词个数,更多相关信息见《shell编程之数值计算》之 _单词统计_ 实例。 $ echo $var | wc -w 5 $ echo "$var" | tr " " "\n" | grep get | uniq -c 1 $ echo "$var" | tr " " "\n" | grep get | wc -l 1   1 2 stringZ=abcABC123ABCabc echo `expr "$stringZ" : '.*'`    # 15 说明:

${}操作符在Bash里头一个“大牛”,能胜任相当多的工作,具体就看看网中人的《shell十三问》之《Shell十三问》之"$(( )) 與 $( ) 還有${ } 差在哪?" 吧。

在一个文本文件的段落之间插入空行

 

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #!/bin/bash # paragraph-space.sh   # 在一个单倍行距的文本文件中插入空行. # Usage: $0 new_file   (大写 --> 小写) # cat file | tr [A-Z] [a-z] > new_file        3、把文件中的数字0-9替换为a-j   # cat file | tr [0-9] [a-j] > new_file        4、删除文件file中出现的"Snail"字符   # cat file | tr -d "Snail" > new_file   【注意】这里,凡是在file文件中出现的'S','n','a','i','l'字符都会被删除!而不是紧紧删除出现的"Snail”字符串。        5、删除文件file中出现的换行'\n'、制表'\t'字符   # cat file | tr -d "\n\t" > new_file   不可见字符都得用转义字符来表示的,这个都是统一的。        6、删除“连续着的”重复字母,只保留第一个   # cat file | tr -s [a-zA-Z] > new_file        7、删除空行   # cat file | tr -s "\n" > new_file        8、删除Windows文件“造成”的'^M'字符   # cat file | tr -d "\r" > new_file 或者 # cat file | tr -s "\r" "\n" > new_file   【注意】这里-s后面是两个参数"\r"和"\n",用后者替换前者        9、用空格符\040替换制表符\011   # cat file | tr -s "\011" "\040" > new_file        10、把路径变量中的冒号":",替换成换行符"\n"   # echo $PATH | tr -s ":" "\n"

 

 

2.4. 插入子串

插入子串:就是在指定的位置插入子串,这个位置可能是某个子串的位置,也可能是从某个文件开头算起的某个长度。通过上面的练习,我们发现这两者之间 实际上是类似的。

公式:插入子串=把"old子串"替换成"old子串+new子串"或者"new子串+old子串"

概要示例::下面在var字符串的空格之前或之后插入一个下划线

// 用{}$ var="get the length of me"$ echo ${var/ /_ }        #在指定字符串之前插入一个字符串get_ the length of me$ echo ${var// /_ }get_ the_ length_ of_ me$ echo ${var/ / _}        #在指定字符串之后插入一个字符串get _the length of me$ echo ${var// / _}get _the _length _of _me

// 其他的还用演示么?这里主要介绍sed怎么用来插入字符吧,因为它的标签功能很有趣$ echo $var | sed -e 's/\( \)/_\1/' #\(和\)将不匹配到的字符串存放为一个标签,按匹配顺序为\1,\2...get_ the length of me$ echo $var | sed -e 's/\( \)/_\1/g'get_ the_ length_ of_ me$ echo $var | sed -e 's/\( \)/\1_/'get _the length of me$ echo $var | sed -e 's/\( \)/\1_/g'get _the _length _of _me

// 看看sed的标签的顺序是不是\1,\2....,看到没?\2和\1掉换位置后,the和get的位置掉换了$ echo $var | sed -e 's/\([a-z]*\) \([a-z]*\) /\2 \1 /g'the get of length me// sed还有专门的插入指令,a和i,分别表示在匹配的行后和行前插入指定字符$ echo $var | sed '/get/a test'get the length of metest$ echo $var | sed '/get/i test'testget the length of me

2.5. 删除子串

删除子串:应该很简单了吧,把子串替换成“空”(什么都没有)不就变成了删除么。还是来简单复习一下替换吧。

概要示例::把var字符串中所有的空格给删除掉。

鼓励: 这样一替换不知道变成什么单词啦,谁认得呢?但是中文却是连在一起的,所以中文有多难,你想到了么?原来你也是个语言天才,而英语并不可怕,你有学会它的 天赋,只要你有这个打算。

// 再用{}$ echo ${var// /}getthelengthofme// 再用awk$ echo $var | awk '{gsub(" ","",$0); printf("%s\n", $0);}'// 再用sed$ echo $var | sed 's/ //g'getthelengthofme// 还有更简单的tr命令,tr也可以把" "给删除掉,看$ echo $var | tr -d " "getthelengthofme

如果要删除掉第一个空格后面所有的字符串该怎么办呢?还记得{}的#和%用法么?如果不记得,回到这一节的还头开始复习吧。(实际上删除子串和取子串未尝 不是两种互补的运算呢,删除掉某些不想要的子串,也就同时取得另外那些想要的子串——这个世界就是一个“二元”的世界,非常有趣)

2.6. 子串比较

这个很简单:还记得test命令的用法么?man test。它可以用来判断两个字符串是否相等的。另外,你发现了“字符串是否相等”和“字符串能否跟另外一个字符串匹配"两个问题之间的关系吗?如果两个 字符串完全匹配,那么这两个字符串就相等了。所以呢,上面用到的字符串匹配方法,也同样可以用到这里。

2.7. 子串排序

差点忘记这个重要的内容了,子串排序可是经常用到的,常见的有按字母序、数字序等正序或反序排列。sort命令可以用来做这个工作,它和其他行处理 命令一样,是按行操作的,另外,它类似cut和awk,可以指定分割符,并指定需要排序的列。

$ var="get the length of me"$ echo $var | tr ' ' '\n' | sort   #正序排getlengthmeofthe$ echo $var | tr ' ' '\n' | sort -r #反序排theofmelengthget2.7. 子串进制转换

如果字母和数字字符用来计数,那么就存在进制转换的问题。在数值计算一节的回复资料里,我们已经介绍了bc命令,这里再简单的复习一下。

$ echo "ibase=10;obase=16;10" | bcA

说明:ibase指定输入进制,obase指出输出进制,这样通过调整ibase和obase,你想怎么转就怎么转啦!

2.7. 子串编码转换

什么是字符编码?这个就不用介绍了吧,看过那些乱七八糟显示的网页么?大多是因为浏览器显示时的”编码“和网页实际采用的”编码“不一致导致的。字 符编码通常是指把一序列”可打印“字符转换成二进制表示,而字符解码呢则是执行相反的过程,如果这两个过程不匹配,则出现了所谓的”乱码“。

为了解决”乱码“问题呢?就需要进行编码转换。在linux下,我们可以使用iconv这个工具来进行相关操作。这样的情况经常在不同的操作系统之 间移动文件,不同的编辑器之间交换文件的时候遇到,目前在windows下常用的汉字编码是gb2312,而在linux下则大多采用utf8。

$ nihao_gb2312=$(echo "你好" | iconv -f utf8 -t gb2312)$ echo $nihao_gb2312? ? ? ? $ nihao_utf8=$(echo $nihao_gb2312 | iconv -f gb2312 -t utf8)$ PS1="$ "$ echo $nihao_utf8你好

说明:我的终端默认编码是utf8,所以结果如上。

3. 字符串操作范例

实际上,在用Bash编程时,大部分时间都是在处理字符串,因此把这一节熟练掌握非常重要。

3.1 处理一个非常有意义的字符串:URL地址

范例演示:处理URL地址

URL 地址(URL(Uniform Resoure Locator:统一资源定位器)是WWW页的地址)几乎是我们日常生活的玩伴,我们已经到了无法离开它的地步啦,对它的操作很多,包括判断URL地址的 有效性,截取地址的各个部分(服务器类型、服务器地址、端口、路径等)并对各个部分进行进一步的操作。

下面我们来具体处理这个URL地址:ftp://anonymous:[email protected]/software/scim-1.4.7.tar.gz

$ url="ftp://anonymous:[email protected]/software/scim-1.4.7.tar.gz "// 匹配URL地址,判断URL地址的有效性$ echo $url | grep "ftp://[a-z]*:[a-z]*@[a-z\./ -]*"// 截取服务器类型$ echo ${url%%:*}ftp$ echo $url | cut -d":" -f 1ftp// 截取域名$ tmp=${url##*@} ; echo ${tmp%%/*}mirror.lzu.edu.cn// 截取路径$ tmp=${url##*@} ; echo ${tmp%/*}mirror.lzu.edu.cn/software// 截取文件名$ basename $urlscim-1.4.7.tar.gz$ echo ${url##*/}scim-1.4.7.tar.gz// 截取文件类型(扩展名)$ echo $url | sed -e 's/.*[0-9].\(.*\)/\1/g'tar.gz

有了上面的知识,我们就可以非常容易地进行这些工作啦:修改某个文件的文件名,比如调整它的编码,下载某个网页里头的所有pdf文档等。这些就作为练习自 己做吧,如果遇到问题,可以在回帖交流。相应地可以参考这个例子:

[1] 用脚本下载某个网页中的英文原著(pdf文档)http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1228.html

3.2 处理格式化的文本:/etc/passwd

平时做工作,大多数时候处理的都是一些“格式化”的文本,比如类似/etc/passwd这样的有固定行和列的文本,也有类似tree命令输出的那 种具有树形结构的文本,当然还有其他具有特定结构的文本。

关于树状结构的文本的处理,可以考虑看看这两个例子:[1] 用AWK转换树形数据成关系表http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1260.html [2] 用Graphviz进行可视化操作──绘制函数调用关系图http://oss.lzu.edu.cn/blog/blog.php?do_showone/tid_1425.html 实际上,只要把握好特性结构的一些特点,并根据具体的应用场合,处理起来就不会困难。

下面我们来介绍具体有固定行和列的文本的操作,以/etc/passwd文件为例。关于这个文件的帮忙和用户,请通过man 5 passwd查看。下面我们对这个文件以及相关的文件进行一些有意义的操作。

// 选取/etc/passwd文件中的用户名和组ID两列$ cat /etc/passwd | cut -d":" -f1,4// 选取/etc/group文件中的组名和组ID两列$ cat /etc/group | cut -d":" -f1,3// 如果想找出所有用户所在的组,怎么办?$ join -o 1.1,2.1 -t":" -1 4 -2 3 /etc/passwd /etc/grouproot:rootbin:bindaemon:daemonadm:admlp:lppop:popnobody:nogroupfalcon:users// 先解释一下:join命令用来连接两个文件,有点类似于数据库的两个表的连接。-t指定分割符,"-1 4 -2 3"指定按照第一个文件的第4列和第二个文件的第3列,即组ID进行连接,"-o 1.1,2.1"表示仅仅输出第一个文件的第一列和第二个文件的第一列,这样就得到了我们要的结果,不过,可惜的是,这个结果并不准确,再进行下面的操 作,你就会发现:$ cat /etc/passwd | sort -t":" -n -k 4 > /tmp/passwd$ cat /etc/group | sort -t":" -n -k 3 > /tmp/group$ join -o 1.1,2.1 -t":" -1 4 -2 3 /tmp/passwd /tmp/grouphalt:rootoperator:rootroot:rootshutdown:rootsync:rootbin:bindaemon:daemonadm:admlp:lppop:popnobody:nogroupfalcon:usersgames:users// 可以看到这个结果才是正确的,所以以后使用join千万要注意这个问题,否则采取更保守的做法似乎更能保证正确性,更多关于文件连接的讨论见参考资料 [14]

上面涉及到了处理某格式化行中的指定列,包括截取(如SQL的select用法),连接(如SQL的join用法),排序(如SQL的order by用法),都可以通过指定分割符来拆分某个格式化的行,另外,“截取”的做法还有很多,不光是cut,awk,甚至通过IFS指定分割符的read命令 也可以做到,例如:

$ IFS=":"; cat /etc/group | while read C1 C2 C3 C4; do echo $C1 $C3; done

因此,熟悉这些用法,我们的工作将变得非常灵活有趣。

到这里,需要做一个简单的练习,如何把按照列对应的用户名和用户ID转换成按照行对应的,即把类似下面的数据:

$ cat /etc/passwd | cut -d":" -f1,3 --output-delimiter=" "root 0bin 1daemon 2

转换成:

$ cat aroot    bin     daemon 0       1       2

并转换回去,有什么办法呢?记得诸如tr,paste,split等命令都可以使用。

参考方法:*正转换:先截取用户名一列存入文件user,再截取用户ID存入id,再把两个文件用paste -s命令连在一起,这样就完成了正转换。*逆转换:先把正转换得到的结果用split -1拆分成两个文件,再把两个拆分后的文件用tr把分割符"\t"替换成"\n",只有用paste命令把两个文件连在一起,这样就完成了逆转换。

在做shell批处理程序时候,经常会涉及到字符串相关操作。有很多命令语句,如:awk,sed都可以做字符串各种操作。 其实shell内置一系列操作符号,可以达到类似效果,大家知道,使用内部操作符会省略启动外部程序等时间,因此速度会非常的快。

 

一、判断读取字符串值

 

表达式含义 ${var} 变量var的值, 与$var相同     ${var-DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值 * ${var:-DEFAULT} 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 * ${var=DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值 * ${var:=DEFAULT} 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 * ${var+OTHER} 如果var声明了, 那么其值就是$OTHER, 否则就为null字符串 ${var:+OTHER} 如果var被设置了, 那么其值就是$OTHER, 否则就为null字符串 ${var?ERR_MSG} 如果var没被声明, 那么就打印$ERR_MSG * ${var:?ERR_MSG} 如果var没被设置, 那么就打印$ERR_MSG * ${!varprefix*} 匹配之前所有以varprefix开头进行声明的变量 ${!varprefix@} 匹配之前所有以varprefix开头进行声明的变量

 

加入了“*”  的意思是: 如果变量var已经被设置的话, 那么其值就是$var.

 

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [a@localhost ~]$ echo ${abc-'ok'} ok [a@localhost ~]$ echo $abc   [a@localhost ~]$ echo ${abc='ok'} ok [a@localhost ~]$ echo $abc ok   如果abc 没有声明“=" 还会给abc赋值。 [a@localhost ~]$ var1=11;var2=12;var3= [a@localhost ~]$ echo ${!v@}            var1 var2 var3 [a@localhost ~]$ echo ${!v*} var1 var2 var3   ${!varprefix*}与${!varprefix@}相似,可以通过变量名前缀字符,搜索已经定义的变量,无论是否为空值。 表达式含义 ${#string} $string的长度 ${string:position} 在$string中, 从位置$position开始提取子串 ${string:position:length} 在$string中, 从位置$position开始提取长度为$length的子串 ${string#substring} 从变量$string的开头, 删除最短匹配$substring的子串 ${string##substring} 从变量$string的开头, 删除最长匹配$substring的子串 ${string%substring} 从变量$string的结尾, 删除最短匹配$substring的子串 ${string%%substring} 从变量$string的结尾, 删除最长匹配$substring的子串 ${string/substring/replacement} 使用$replacement, 来代替第一个匹配的$substring ${string//substring/replacement} 使用$replacement, 代替所有匹配的$substring ${string/#substring/replacement} 如果$string的前缀匹配$substring, 那么就用$replacement来代替匹配到的$substring ${string/%substring/replacement} 如果$string的后缀匹配$substring, 那么就用$replacement来代替匹配到的$substring

 

说明:"* $substring”可以是一个正则表达式.

 

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 1.长度   [a@localhost ~]$ test='I love china' [a@localhost ~]$ echo ${#test} 12   ${#变量名}得到字符串长度        2.截取字串   [a@localhost ~]$ test='I love china' [a@localhost ~]$ echo ${test:5}     e china [a@localhost ~]$ echo ${test:5:10} e china   ${变量名:起始:长度}得到子字符串        3.字符串删除   [a@localhost ~]$ test='c:/windows/boot.ini' [a@localhost ~]$ echo ${test#/} c:/windows/boot.ini [a@localhost ~]$ echo ${test#*/} windows/boot.ini [a@localhost ~]$ echo ${test##*/} boot.ini   [a@localhost ~]$ echo ${test%/*} c:/windows [a@localhost ~]$ echo ${test%%/*}   ${变量名#substring正则表达式}从字符串开头开始配备substring,删除匹配上的表达式。   ${变量名%substring正则表达式}从字符串结尾开始配备substring,删除匹配上的表达式。   注意:${test##*/},${test%/*} 分别是得到文件名,或者目录地址最简单方法。   4.字符串替换   [a@localhost ~]$ test='c:/windows/boot.ini' [a@localhost ~]$ echo ${test/\//\\} c:\windows/boot.ini [a@localhost ~]$ echo ${test//\//\\} c:\windows\boot.ini   ${变量/查找/替换值} 一个“/”表示替换第一个,”//”表示替换所有,当查找中出现了:”/”请加转义符”\/”表示。 性能比较

 

 

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 在shell中,通过awk,sed,expr 等都可以实现,字符串上述操作。下面我们进行性能比较。   [a@localhost ~]$ test='c:/windows/boot.ini'                       [a@localhost ~]$ time for i in $(seq 10000);do a=${#test};done;             real    0m0.173s user    0m0.139s sys     0m0.004s   [a@localhost ~]$ time for i in $(seq 10000);do a=$(expr length $test);done;        real    0m9.734s user    0m1.628s   速度相差上百倍,调用外部命令处理,与内置操作符性能相差非常大。在shell编程中,尽量用内置操作符或者函数完成。使用awk,sed类似会出现这样结果。

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3